use std::error::Error;
use std::ptr;
mod test_ll3;
mod linked_list;


// https://stackoverflow.com/questions/21747136/how-do-i-print-the-type-of-a-variable-in-rust
// NOTE: must be used for a debug purpose only:
pub fn type_of<T>(_: &T) -> &str {
    std::any::type_name::<T>()
}

// `#[macro_export]` will be exported at the root of the crate
// NOTE: dbg!(var1, var2) is ok, but dbgt! cannot
#[macro_export]
macro_rules! dbgt {
    ($val:expr) => {
        match $val {
            tmp => {
                eprintln!(
                    "[{}:{}] ({}: {}) = {:#?}",
                    file!(),
                    line!(),
                    stringify!($val),
                    $crate::type_of(tmp), // not $val, &tmp
                    &tmp
                );
                tmp
            }
        }
    };
}

// seems no need to support statement
// put block before expr, block belongs to expr?
#[macro_export]
macro_rules! timeit {
    ($loops:expr, $code:block) => {
        let timeit_n = $loops;
        let timeit_start = std::time::Instant::now();
        for _ in 0..timeit_n {
            $code
        }
        let timeit_cost = timeit_start.elapsed();
        println!(
            "[{}:{}] ({} loops, {:?} per loop) {{ ... }}",
            file!(),
            line!(),
            timeit_n,
            timeit_cost / timeit_n
        );
    };
    ($loops:expr, $code:expr) => {
        let timeit_n = $loops;
        let timeit_start = std::time::Instant::now();
        for _ in 0..timeit_n {
            $code;
        }
        let timeit_cost = timeit_start.elapsed();
        println!(
            "[{}:{}] ({} loops, {:?} per loop) {{ {} }}",
            file!(),
            line!(),
            timeit_n,
            timeit_cost / timeit_n,
            stringify!($code)
        );
    };
}

pub struct Config {}

pub struct LinkedList<T> {
    pub value: T,
    pub next: *mut LinkedList<T>,
}

trait DebuggableList {
    fn print_list(&mut self);
}

impl<T> LinkedList<T> {
    pub fn create<Iter>(iter: Iter) -> *mut LinkedList<T>
    where
        Iter: Iterator<Item = T>,
    {
        let mut p: *mut LinkedList<T> = ptr::null_mut();
        let mut root: *mut LinkedList<T> = ptr::null_mut();
        for item in iter {
            let mut temp = LinkedList {
                value: item,
                next: ptr::null_mut(),
            };
            if !p.is_null() {
                unsafe {
                    (*p).next = &mut temp;
                }
            }
            p = &mut temp;
	    println!("{}", p as usize);
            if root.is_null() {
                root = p;
            }
        }
	return root;
    }
}

impl<T> DebuggableList for LinkedList<T>
where
    T: std::fmt::Display,
{
    fn print_list(&mut self) {
        let mut p: *mut LinkedList<T> = &mut *self;
        while !p.is_null() {
            unsafe {
                print!("{} ", (*p).value);
		p = (*p).next;
            }
        }
    }
}

impl Config {
    pub fn new(args: Vec<String>) -> Result<Config, &'static str> {
        if args.len() < 1 {
            return Err("not enough arguments");
        }
        //let query = args[1].clone();
        Ok(Config {})
    }
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let data = vec![11, 77, 65, 22, 33];
    let mut p =LinkedList::create(data.iter());
    unsafe {
	(*p).print_list();
    }
    Ok(())
}
